/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.l2j.gameserver.model.actor.instance;

import java.util.List;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

import javolution.util.FastList;
import net.sf.l2j.gameserver.ThreadPoolManager;
import net.sf.l2j.gameserver.datatables.SkillTable;
import net.sf.l2j.gameserver.handler.ISkillHandler;
import net.sf.l2j.gameserver.handler.SkillHandler;
import net.sf.l2j.gameserver.model.L2Character;
import net.sf.l2j.gameserver.model.L2Party;
import net.sf.l2j.gameserver.model.L2Skill;
import net.sf.l2j.gameserver.network.serverpackets.MagicSkillUser;
import net.sf.l2j.util.Rnd;

public class L2CubicInstance {
	protected static final Logger _log = Logger.getLogger(L2CubicInstance.class
			.getName());
	public static final int STORM_CUBIC = 1;
	public static final int VAMPIRIC_CUBIC = 2;
	public static final int LIFE_CUBIC = 3;
	public static final int VIPER_CUBIC = 4;
	public static final int POLTERGEIST_CUBIC = 5;
	public static final int BINDING_CUBIC = 6;
	public static final int AQUA_CUBIC = 7;
	public static final int SPARK_CUBIC = 8;
	public static final int ATTRACT_CUBIC = 9;
	protected L2PcInstance _owner;
	protected L2Character _target;
	protected int _id;
	protected int _level = 1;
	protected List<Integer> _skills = new FastList<Integer>();
	private Future<?> _disappearTask;
	private Future<?> _actionTask;

	public L2CubicInstance(L2PcInstance owner, int id, int level) {
		_owner = owner;
		_id = id;
		_level = level;
		switch (_id) {
		case STORM_CUBIC:
			_skills.add(4049);
			break;
		case VAMPIRIC_CUBIC:
			_skills.add(4050);
			break;
		case LIFE_CUBIC:
			_skills.add(4051);
			_disappearTask = ThreadPoolManager.getInstance().scheduleGeneral(
					new Disappear(), 3600000); // disappear
			// in 60
			// mins
			doAction(_owner);
			break;
		case VIPER_CUBIC:
			_skills.add(4052);
			break;
		case POLTERGEIST_CUBIC:
			_skills.add(4053);
			_skills.add(4054);
			_skills.add(4055);
			break;
		case BINDING_CUBIC:
			_skills.add(4164);
			break;
		case AQUA_CUBIC:
			_skills.add(4165);
			break;
		case SPARK_CUBIC:
			_skills.add(4166);
			break;
		case ATTRACT_CUBIC:
			_skills.add(5115);
			_skills.add(5116);
			break;
		}
		if (_disappearTask == null)
			_disappearTask = ThreadPoolManager.getInstance().scheduleGeneral(
					new Disappear(), 1200000); // disappear
		// in
		// 20
		// mins
	}

	public void doAction(L2Character target) {
		if (_target == target)
			return;
		stopAction();
		_target = target;
		switch (_id) {
		case STORM_CUBIC:
			_actionTask = ThreadPoolManager.getInstance()
					.scheduleEffectAtFixedRate(new Action(12), 0, 10000);
			break;
		case VAMPIRIC_CUBIC:
			_actionTask = ThreadPoolManager.getInstance()
					.scheduleEffectAtFixedRate(new Action(8), 0, 15000);
			break;
		case VIPER_CUBIC:
			_actionTask = ThreadPoolManager.getInstance()
					.scheduleEffectAtFixedRate(new Action(30), 0, 20000);
			break;
		case POLTERGEIST_CUBIC:
			_actionTask = ThreadPoolManager.getInstance()
					.scheduleEffectAtFixedRate(new Action(30), 0, 8000);
			break;
		case BINDING_CUBIC:
		case AQUA_CUBIC:
		case SPARK_CUBIC:
			_actionTask = ThreadPoolManager.getInstance()
					.scheduleEffectAtFixedRate(new Action(30), 0, 8000);
			break;
		case LIFE_CUBIC:
			_actionTask = ThreadPoolManager.getInstance()
					.scheduleEffectAtFixedRate(new Heal(50), 0, 30000);
			break;
		case ATTRACT_CUBIC:
			_actionTask = ThreadPoolManager.getInstance()
					.scheduleEffectAtFixedRate(new Action(30), 0, 8000);
			break;
		}
	}

	public int getId() {
		return _id;
	}

	public void setLevel(int level) {
		_level = level;
	}

	public void stopAction() {
		_target = null;
		if (_actionTask != null) {
			_actionTask.cancel(true);
			_actionTask = null;
		}
	}

	public void cancelDisappear() {
		if (_disappearTask != null) {
			_disappearTask.cancel(true);
			_disappearTask = null;
		}
	}

	private class Action implements Runnable {
		private int _chance;

		Action(int chance) {
			_chance = chance;
			// run task
		}

		public void run() {
			if (_owner.isDead() || _target.isDead()
					|| _owner.getTarget() != _target) {
				stopAction();
				if (_owner.isDead()) {
					_owner.delCubic(_id);
					_owner.broadcastUserInfo();
					cancelDisappear();
				}
				return;
			}
			if (_target != null)
				try {
					if (Rnd.get(1, 100) < _chance) {
						L2Skill skill = SkillTable.getInstance().getInfo(
								_skills.get(Rnd.get(_skills.size())), _level);
						if (skill != null) {
							L2Character[] targets = { _target };
							ISkillHandler handler = SkillHandler.getInstance()
									.getSkillHandler(skill.getSkillType());
							int x, y, z;
							// temporary range check until real behavior of
							// cubics is known/coded
							int range = _target.getTemplate().collisionRadius + 400; // skill.getCastRange();
							x = _owner.getX() - _target.getX();
							y = _owner.getY() - _target.getY();
							z = _owner.getZ() - _target.getZ();
							if (x * x + y * y + z * z <= range * range) {
								if (handler != null)
									handler.useSkill(_owner, skill, targets);
								else
									skill.useSkill(_owner, targets);
								MagicSkillUser msu = new MagicSkillUser(_owner,
										_target, skill.getId(), _level, 0, 0);
								_owner.broadcastPacket(msu);
							}
						}
					}
				} catch (Exception e) {
					_log.log(Level.SEVERE, "", e);
				}
		}
	}

	private class Heal implements Runnable {
		private int _chance;

		Heal(int chance) {
			_chance = chance;
			// run task
		}

		public void run() {
			if (_owner.isDead()) {
				stopAction();
				_owner.delCubic(_id);
				_owner.broadcastUserInfo();
				cancelDisappear();
				return;
			}
			try {
				if (Rnd.get(1, 100) < _chance) {
					L2Skill skill = SkillTable.getInstance().getInfo(
							_skills.get(Rnd.get(_skills.size())), _level);
					if (skill != null) {
						L2Character target, caster;
						target = null;
						if (_owner.isInParty()) {
							caster = _owner;
							L2PcInstance player = _owner;
							L2Party party = player.getParty();
							double percentleft = 100.0;
							if (party != null) {
								// Get all visible objects in a spheric area
								// near the L2Character
								// Get a list of Party Members
								List<L2PcInstance> partyList = party
										.getPartyMembers();
								L2Character partyMember = null;
								int x, y, z;
								// temporary range check until real behavior of
								// cubics is known/coded
								int range = 400; // skill.getCastRange();
								for (int i = 0; i < partyList.size(); i++) {
									partyMember = partyList.get(i);
									if (!partyMember.isDead()) {
										// if party member not dead, check if he
										// is in castrange of heal cubic
										x = caster.getX() - partyMember.getX();
										y = caster.getY() - partyMember.getY();
										z = caster.getZ() - partyMember.getZ();
										if (x * x + y * y + z * z > range
												* range)
											continue;
										// member is in cubic casting range,
										// check if he need heal and if he have
										// the lowest HP
										if (partyMember.getCurrentHp() < partyMember
												.getMaxHp())
											if (percentleft > partyMember
													.getCurrentHp()
													/ partyMember.getMaxHp()) {
												percentleft = partyMember
														.getCurrentHp()
														/ partyMember
																.getMaxHp();
												target = partyMember;
											}
									}
								}
							}
						} else if (_owner.getCurrentHp() < _owner.getMaxHp())
							target = _owner;
						if (target != null) {
							L2Character[] targets = { target };
							ISkillHandler handler = SkillHandler.getInstance()
									.getSkillHandler(skill.getSkillType());
							if (handler != null)
								handler.useSkill(_owner, skill, targets);
							else
								skill.useSkill(_owner, targets);
							MagicSkillUser msu = new MagicSkillUser(_owner,
									target, skill.getId(), _level, 0, 0);
							_owner.broadcastPacket(msu);
						}
					}
				}
			} catch (Exception e) {
			}
		}
	}

	private class Disappear implements Runnable {
		Disappear() {
			// run task
		}

		public void run() {
			stopAction();
			_owner.delCubic(_id);
			_owner.broadcastUserInfo();
		}
	}
}
